import * as check_ from '../../util/check_';
import * as json_ from '../../util/json_';
import * as str_ from '../../util/str_';
import * as array_ from '../../util/array_';
import { DBType } from '../../db/DB';

export class ParamKey {
    key: string;
    value;
    dynamic: boolean;
    constructor(key, value, dynamic) {
        this.key = key;
        this.dynamic = dynamic;
        this.value = value;
    }

    setValue(value) {
        this.value = value;
    }

    getValue() {
        if (check_.isString(this.value)) return this.value;
        if (this.dynamic && check_.isFunction(this.value)) {
            return this.value();
        }
        return '';
    }

    render(content) {
        let regx = new RegExp('\\$\\{' + this.key + '\\}', 'gi');
        if (regx.test(content)) {
            content = content.replace(regx, this.getValue());
        }
        return content;
    }

    static parseJson(json) {
        return json_.parseJsonRender(json, function ({key, value, dynamic}) {
            return new ParamKey(key, value, dynamic);
        });
    }
}

export class TableConfig {
    prefix: string;
    projectSplit: string;
    moduleSplit: string;
    separator: string;
    constructor(prefix, projectSplit, moduleSplit, separator) {
        this.prefix = prefix;
        this.projectSplit = projectSplit;
        this.moduleSplit = moduleSplit;
        this.separator = separator;
    }

    getProject(tableName) {
        let project = '';
        tableName = tableName.substr(this.prefix.length);
        if (this.projectSplit) {
            project = tableName.split(this.separator)[0];
        }
        return project;
    }

    getModule(tableName) {
        let module = '', start = this.prefix.length;
        if(this.projectSplit){
            start += this.getProject(tableName).length + this.separator.length;
        }

        tableName = tableName.substr(start);
        if (this.moduleSplit) {
            module = tableName.split(this.separator)[0];
        }
        return module;
    }

    getEntity(tableName) {
        let start = this.prefix.length;
        if(this.projectSplit){
            start += this.getProject(tableName).length + this.separator.length;
        }
        if(this.moduleSplit){
            start += this.getModule(tableName).length + this.separator.length;
        }
        if(start > tableName.length){
            console.error('超出指定数组长度, tableName: %s, start: %d', tableName, start)
        }
        return tableName.substr(start);
    }

    static parseJson({prefix, projectSplit, moduleSplit, separator}) {
        return new TableConfig(prefix, projectSplit, moduleSplit, separator);
    }
}

export class BaseDtoConfig {
    hasBaseDto: boolean;
    baseColumns: string[];
    deleteColumn: string;
    constructor(hasBaseDto, baseColumns, deleteColumn) {
        this.hasBaseDto = hasBaseDto;
        this.baseColumns = baseColumns;
        this.deleteColumn = deleteColumn;
    }
    static parseJson({hasBaseDto,baseColumns, deleteColumn}) {
        return new BaseDtoConfig(hasBaseDto,baseColumns, deleteColumn);
    }
}
export class FieldFillConfig {
    hasFieldFill: boolean;
    insertFillColumns: string[];
    insertUpdateFillColumns: string[];
    updateFillColumns: string[];
    constructor(hasFieldFill, insertFillColumns, insertUpdateFillColumns, updateFillColumns) {
        this.hasFieldFill = hasFieldFill;
        this.insertFillColumns = insertFillColumns;
        this.insertUpdateFillColumns = insertUpdateFillColumns;
        this.updateFillColumns = updateFillColumns;
    }
    static parseJson({hasFieldFill, insertFillColumns, insertUpdateFillColumns, updateFillColumns}) {
        return new FieldFillConfig(hasFieldFill, insertFillColumns, insertUpdateFillColumns, updateFillColumns);
    }
}
export class RangeFieldConfig {
    hasRangeField: boolean;
    rangeFieldColumns: string[];
    rangeFieldSuffixKey: string;
    constructor(hasRangeField, rangeFieldColumns, rangeFieldSuffixKey) {
        this.hasRangeField = hasRangeField;
        this.rangeFieldColumns = rangeFieldColumns;
        this.rangeFieldSuffixKey = rangeFieldSuffixKey;
    }
    static parseJson({hasRangeField, rangeFieldColumns, rangeFieldSuffixKey}) {
        return new RangeFieldConfig(hasRangeField, rangeFieldColumns, rangeFieldSuffixKey);
    }
}
export class FeishuBiTableFieldConfig {
    excludes: string[];
    constructor(excludes) {
        this.excludes = excludes;
    }
    static parseJson({excludes}) {
        return new FeishuBiTableFieldConfig(excludes);
    }
}

export class GenterateTable {
    tablePrefix: string;
    includes: string[];
    excludes: string[];
    constructor(tablePrefix = '', includes = [], excludes = []) {
        this.tablePrefix = tablePrefix;
        this.includes = includes;
        this.excludes = excludes;
    }

    setTablePrefix(tablePrefix) {
        this.tablePrefix = tablePrefix;
    }

    setIncludes(includes) {
        this.includes = includes;
    }

    setExcludes(excludes) {
        this.excludes = excludes;
    }

    getRemainTables(tables) {
        if (!tables) return [];
        let remainTables = Array.from(tables);
        let that = this;
        if (!check_.isEmpty(this.tablePrefix)) {
            tables.forEach(function (item) {
                if (!item.startsWith(that.tablePrefix)) {
                    array_.removeItem(remainTables, item);
                }
            })
        }
        if (!check_.isEmpty(this.includes)) {
            tables.forEach(function (item) {
                if (that.includes.indexOf(item) == -1) {
                    array_.removeItem(remainTables, item);
                }
            })
        } else {
            if (!check_.isEmpty(this.excludes)) {
                tables.forEach(function (item) {
                    if (that.excludes.indexOf(item) > -1) {
                        array_.removeItem(remainTables, item);
                    }
                })
            }
        }
        return remainTables;
    }

    getRemainEntities(entities) {
        if (!entities) return [];
        let remainEntities = Array.from(entities);
        let that = this;
        if (!check_.isEmpty(this.tablePrefix)) {
            entities.forEach(function (item) {
                if (!item.tableName.startsWith(that.tablePrefix)) {
                    array_.removeItem(remainEntities, item);
                }
            })
        }
        if (!check_.isEmpty(this.includes)) {
            entities.forEach(function (item) {
                if (that.includes.indexOf(item.tableName) === -1) {
                    array_.removeItem(remainEntities, item);
                }
            })
        } else {
            if (!check_.isEmpty(this.excludes)) {
                entities.forEach(function (item) {
                    if (that.excludes.indexOf(item.tableName) > -1) {
                        array_.removeItem(remainEntities, item);
                    }
                })
            }
        }

        return remainEntities;
    }

    static parseJson({tablePrefix, includes, excludes}) {
        return new GenterateTable(tablePrefix, includes, excludes);
    }
}

export class Strategy {
    dbType: DBType;
    fileOverridable: boolean;
    camelCase: boolean;
    project: string;
    module: string;
    paramKeys: ParamKey[];
    tableConfig: TableConfig;
    baseDtoConfig: BaseDtoConfig;
    fieldFillConfig: FieldFillConfig;
    rangeFieldConfig: RangeFieldConfig;
    feishuBiTableFieldConfig: FeishuBiTableFieldConfig;
    generateTable: GenterateTable;
    constructor(fileOverridable, camelCase, project, module, paramKeys, tableConfig, baseDtoConfig, fieldFillConfig, rangeFieldConfig, feishuBiTableFieldConfig, generateTable, dbType) {
        this.fileOverridable = fileOverridable;
        this.camelCase = camelCase;
        this.project = project;
        this.module = module;
        this.paramKeys = ParamKey.parseJson(paramKeys);
        this.tableConfig = TableConfig.parseJson(tableConfig);
        this.baseDtoConfig = BaseDtoConfig.parseJson(baseDtoConfig);
        this.fieldFillConfig = FieldFillConfig.parseJson(fieldFillConfig);
        this.rangeFieldConfig = RangeFieldConfig.parseJson(rangeFieldConfig);
        this.feishuBiTableFieldConfig = FeishuBiTableFieldConfig.parseJson(feishuBiTableFieldConfig);

        this.generateTable = GenterateTable.parseJson(generateTable);
        this.dbType = dbType;
    }

    renderCamelCase(value, firstUpper = true) {
        if (value && this.camelCase) {
            let camel = str_.camelCase(value);
            if (firstUpper) {
                camel = camel[0].toUpperCase() + camel.substr(1);
            }
            return camel;
        }
        return value;
    }

    getProject(tableName) {
        let tableProject = this.tableConfig.getProject(tableName);
        if (this.project && this.project !== tableProject) {
            tableProject = this.project;
        }
        return tableProject;
    }

    getModule(tableName?: string) {
        return this.module || 'common';
    }

    getSubModel(tableName) {
        return this.tableConfig.getModule(tableName).toLowerCase();
    }

    getEntity(tableName) {
        let name = this.tableConfig.getEntity(tableName).toLowerCase();
        return this.renderCamelCase(name);
    }

    getEntityBeanName(tableName) {
        let beanName = this.tableConfig.getEntity(tableName).toLowerCase();
        return this.renderCamelCase(beanName, false);
    }

    getEntiyNameLower(tableName) {
        let name = this.tableConfig.getEntity(tableName).toLowerCase();
        const beanName = this.renderCamelCase(name, false);
        return str_.lowercase(beanName);
    }

    getEntiyNameHyphen(tableName) {
        let name = this.tableConfig.getEntity(tableName).toLowerCase();
        const beanName = str_.hyphenCase(name, '-');
        return str_.lowercase(beanName);
    }

    static parseJson({fileOverridable, camelCase, project, module, paramKey, tableConfig, baseDtoConfig, fieldFillConfig, rangeFieldConfig, feishuBiTableFieldConfig, generateTable}, db_type) {
        return new Strategy(fileOverridable, camelCase, project, module, paramKey, tableConfig, baseDtoConfig, fieldFillConfig, rangeFieldConfig, feishuBiTableFieldConfig, generateTable, db_type);
    }
}
